import 'package:flutter/material.dart';

class PulsingCircle extends StatefulWidget {
  const PulsingCircle({super.key});

  @override
  State<PulsingCircle> createState() => _PulsingCircleState();
}

class _PulsingCircleState extends State<PulsingCircle>
    with SingleTickerProviderStateMixin {
  late AnimationController _controller;
  late Animation<double> _sizeAnimation;

  @override
  void initState() {
    super.initState();
    _controller = AnimationController(
      duration: const Duration(milliseconds: 1000),
      vsync: this,
    )..repeat(reverse: true); // 循环往复

    _sizeAnimation = Tween<double>(begin: 50, end: 150).animate(_controller);
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    // ！！！ 注意 ！！！
    // 这里没有调用 setState，但依然会重建。
    // 因为 _sizeAnimation 是一个 Listenable，它在 AnimationController 的驱动下，
    // 每一帧都会变化，并通知它的监听器（这个 Widget 被隐式添加为监听器）。
    return AnimatedBuilder(
      animation: _sizeAnimation,
      builder: (context, child) {
        // 每一帧都会走到这里，构建一个新的 CustomCircle
        return CustomCircle(size: _sizeAnimation.value);
      },
    );
  }
}

class CustomCircle extends LeafRenderObjectWidget {
  final double size;

  const CustomCircle({super.key, required this.size});

  @override
  RenderObject createRenderObject(BuildContext context) {
    return RenderCustomCircle(size: size);
  }

  @override
  void updateRenderObject(BuildContext context, RenderCustomCircle renderObject) {
    // 这个方法的调用时机：
    // 动画每一帧产生一个新的值，导致 AnimatedBuilder 的 builder 函数执行，
    // 创建了一个新的 CustomCircle(size: newValue) widget。
    // Flutter 框架随后调用此方法来更新 RenderObject。
    // 这个过程一秒钟会发生 60 次（60fps）！
    renderObject._size = size;
  }
}

class RenderCustomCircle extends RenderBox {
  double _size;

  RenderCustomCircle({required double size}) : _size = size;

  double get mSize => _size;
  set mSize(double value) {
    if (_size == value) return;
    _size = value;
    markNeedsLayout(); // 尺寸变了，需要重新布局
  }

  @override
  void performLayout() {
    size = Size(mSize, mSize);
  }

  @override
  void paint(PaintingContext context, Offset offset) {
    final Paint paint = Paint()..color = Colors.purple;
    final center = offset + Offset(_size / 2, _size / 2);
    context.canvas.drawCircle(center, _size / 2, paint);
  }
}